<?php

/**
 * Abstract class heartbeataccess
 * This base class has final template methods which are
 * used by the derived concretes. The HeartbeatAccess is a
 * state object that is given to the HeartbeatMessageBuilder to
 * set the access to the current request.
 */
abstract class HeartbeatAccess {

  protected $_whoisuser_types = array(
    self::TYPE_ACTOR => 'Acting user',
    self::TYPE_USER_PROFILE => 'Requested user (from url)'
  );
  protected $_whoisuser_type = self::TYPE_ACTOR;
  protected $_uid = 0;
  protected $_errors = array();
  protected $_has_errors = FALSE;
  protected $_offset_sql = 0;
  protected $_page = FALSE;
  public $stream = NULL;

  const TYPE_ACTOR = 0;
  const TYPE_USER_PROFILE = 1;

  /**
   * Constructor
   *
   * @param $type Array with the variable version of the requested object
   * @param $page Boolean to indicate if this is a page view
   */
  public function __construct($type, $page = FALSE) {
    global $user;

    $this->_page = $page;

    $this->stream = new HeartbeatStream($type);
    $this->stream->setItemsMax($this->_page);

    // Assign the who-is-user type
    $this->_whoisuser_type = variable_get('heartbeat_show_user_profile_messages_' . strtolower(get_class($this)), self::TYPE_ACTOR);

    $this->_uid = $user->uid;
    if ($this->_whoisuser_type == self::TYPE_USER_PROFILE) {
      if(arg(0) == 'user' && is_numeric(arg(1)) && !arg(1)) {
        $this->_uid = arg(1);
      }
    }

    // Populate the user object with a users relations
    // These are fetched from all other modules implementing
    // hook_heartbeat_related_uids
    $user->heartbeat_relations = heartbeat_get_related_uids($this->_uid);

    // Set info properties
    $this->stream->uid = $this->_uid;
    $this->stream->user_relations = $user->heartbeat_relations;
  }

  /**
   * setError
   *
   * @param $text String of found error
   * @return void
   */
  protected function setError($text) {
    $this->_errors[] = $text;
    $this->_has_errors = TRUE;
  }

  /**
   * hasAccess
   *
   * @param $text
   * @return boolean to indicate the access to the stream
   */
  protected function hasAccess() {
    return TRUE;
  }

  /**
   * hasErrors
   *
   * @return boolean has errors
   */
  public function hasErrors() {
    return $this->_has_errors;
  }

  /**
   *
   * @param $text
   * @return unknown_type
   */
  public function getErrors() {
    return $this->_errors;
  }

  /**
   * Get HeartbeatStream object with all configurations
   * @return HeartbeatStream object
   */
  public function getStream() {
    return $this->stream;
  }

  /**
   *
   * @param $text
   * @return Array of messages
   */
  public function fetchMessages() {

    $heartbeat = $this->prepareStream();

    // Call to a hook method to adapt sql and thus result
    $heartbeat = $this->dressUpMessages($heartbeat);

    return $this->hasAccess() ? $this->finishMessages($heartbeat) : array();
  }

  /**
   * resultSql
   *
   * @param $sql String query to extend the current query
   * @param $values Array of values to substitute in the query extension
   * @return array results
   */
  protected function resultSql($sql = '', $values = array()) {

    $args = array();
    $args[] = $this->stream->sql_start . $sql . $this->stream->sql_end;
    $args[] = $this->stream->language;

    if ($this->stream->latest_uaid > 0) {
      $args[] = $this->stream->latest_uaid;
    }
    else {
      $args[] = $this->stream->offset_sql;
      // Limit the query from offset time to a month earlier
      $args[] = $this->stream->offset_sql - variable_get('heartbeat_activity_log_cron_delete', 2678400);
    }

    $args = array_merge($args, $values);

    // The cron limits results to a maximum
    $result = call_user_func_array('db_query', $args);

    $results = array();
    while($heartbeat_row = db_fetch_object($result) ) {
      $results[$heartbeat_row->uaid] = new HeartbeatActivity($heartbeat_row);
    }

    $stream = heartbeat_stream_load($this->getAccess());
    if (isset($stream['display_filters']) &&  $stream['display_filters'] == 1) {
      $this->stream->display_filters = TRUE;
      $this->stream->filters = $stream['filters'];
    }

    return $results;
  }

  /**
   * prepareStream
   *
   * @param $text
   * @return HeartbeatParser object
   */
  protected function prepareStream() {

    global $user, $language;
    $this->stream->offset_sql = $this->_offset_sql;
    $this->stream->language = $language->language;

    // Sql parts
    $this->stream->sql_start = "SELECT
      hm.message AS 'message_orig', hm.message_concat AS 'message_concat_orig',
      hm.attachments AS 'attachments', hm.concat_args, hm.perms,
      ua.*,
      1 AS 'count'
    FROM {heartbeat_activity} ua
    LEFT JOIN {heartbeat_messages} hm ON ua.message_id = hm.message_id
    WHERE
      ua.language = '%s'
    ";

    // Calls with an offset uaid will fetch newer messages
    if ($this->stream->latest_uaid > 0) {
      $this->stream->sql_start .= "AND (ua.uaid > %d )";
    }
    else {
      $this->stream->sql_start .= "AND (ua.timestamp < %d AND ua.timestamp > %d )";
    }

    $this->stream->sql_end = " ORDER BY timestamp DESC";

    $heartbeat = $this->createHeartbeatParser();

    return $heartbeat;
  }

  /**
   * createHeartbeatParser
   *
   * @return HeartbeatParser object
   */
  final protected function createHeartbeatParser() {
    $heartbeat = HeartbeatParser::instantiate($this->getAccess());
    return $heartbeat;
  }

  /**
   * setOffsetSql
   *
   * @param $offset integer to set the offset time
   * @return void
   */
  final public function setOffsetSql($offset = 0) {
    if ($offset == 0) {
      $offset = $_SERVER['REQUEST_TIME'];
    }
    $this->_offset_sql = $offset;
  }

  /**
   *
   * @param $text
   * @return HeartbeatParser object
   */
  protected function dressUpMessages(HeartbeatParser $heartbeat) {
    $heartbeat->raw_messages = $this->resultSql();
  }

  /**
   * finishMessages
   *
   * @param HeartbeatParser $heartbeat Parser object
   * @return HeartbeatParser object
   */
  protected function finishMessages(HeartbeatParser $heartbeat) {
    return $heartbeat;
  }

  /**
   * addViewQuery
   *
   * @param $view Object View
   * @return void
   */
  public function addViewQuery(&$view) {
    // Nothing for base class
  }

  /**
   * getAccess
   *
   * @return string Stream
   */
  final public function getAccess() {
    return strtolower(get_class($this));
  }
}
